分类
联系方式
  1. 新浪微博
  2. E-mail

Flutter js 之 QuickJsRuntime2

QuickJsRuntime2 是 flutter_js 对 QuickJS 引擎的封装,在 Android 下默认为 QuickJS,允许手动切换为 JavaScriptCore。

本文主要研究该类中一些核心功能的实现。

evaluate 资源释放

该类的 evaluate 方法用于执行 JavaScript 代码。

其内部通过 QuickJS 的 jsEval API 实现。

并通过内置的 _jsToDart 方法,实现将结果从 JS 转为 Dart 类型。

资源释放问题

release global context group on dispose for jscore and close runtime for quickjs by wfeng1 · Pull Request #104 · abner/flutter_js (github.com)

这个 PR 指出,evaluate 的结果用完后没有释放,需要手动释放:

jsFreeValue(ctx,jsval);

如果不加这行代码,会导致内存泄漏。

引擎释放

QuickJsRuntime2 的 dispose 方法为空,没有资源释放逻辑,而是作为一个 TODO 待后续完成。

release global context group on dispose for jscore and close runtime for quickjs by wfeng1 · Pull Request #104 · abner/flutter_js (github.com)

该 PR 同样给出了引擎的释放逻辑:

  @override
  void dispose() {
    try {
      port.close(); // stop dispatch loop
      close(); // close engine
    } on JSError catch (e) {
      print(e); // catch reference leak exception
    }
  }

释放报错

我实用上述代码,在释放时遇到了 QuickJS 报错:

quickjs.c:2019: void JS_FreeRuntime(JSRuntime *): assertion "list_empty(&rt->gc_obj_list)" failed.

通过下面两个 issue 可以看出:

如果有底层内存泄漏,QuickJS 在释放时会报这个错误。

经过验证,这里的报错与 evaluate 中的 jsFreeValue 是相关的:

  • 如果我在 evaluate 时没有 jsFreeValue,在引擎释放时,断言发现有泄漏对象报错
  • 如果我在 evaluate 时完成 jsFreeValue,在引擎释放时,可以释放成功

Reference Leak

在解决了释放报错问题后,在释放 QuickJS 引擎时,捕获到一处异常:

[  +11 ms] I/flutter (11005): QuickJsRuntime2 dispose jsError = reference leak:
[        ] I/flutter (11005):     ADDR         REF      TYPE            PROP
[        ] I/flutter (11005):   743331434       1       _JSFunction     (key, val) => { this[key] = val; } ...
[        ] I/flutter (11005):   77684463        1       _JSFunction     function FLUTTER_NATIVEJS_MakeQuerablePromise(promise) { ...
[        ] I/flutter (11005):   836704696       1       _JSFunction     (key, val) => { this[key] = val; } ...
[        ] I/flutter (11005):   285986741       1       _JSFunction     function bound tab2Tab1() { ...
[        ] I/flutter (11005):   911571841       1       _JSFunction     function bound tab2Tab2() { ...
[        ] I/flutter (11005):   755609514       1       _JSFunction     function bound tab2Tab3() { ...

这条异常打印出了一个排版比较整齐的表格,里面给出的是在引擎销毁时仍然没有被释放的对象。

这行 log 是由 flutter_js 库的 quickjs/ffi.dart 的 jsFreeRuntime 方法中实现。

是否会导致内存泄漏?答案是不会,因为 jsFreeRuntime 方法中,从底层对这些变量进行了释放:ref.destroy()。